import * as THREE from "three";

import eventBus from "../EventBus";
import {
  THREE_CANVAS_DELETE,
  THREE_CANVAS_REFRESH
} from "../EventBus/events";

import {
  createBox3
} from "../Tools/ThreeTools";

function createCubeByPositionAndSize(x, y, z, a, b, h) {
  // 验证参数是否合法
  if (arguments.length !== 6 && arguments.length !== 3) return null;
  if (arguments.length === 3) {
    [a, b, h] = [1, 1, 1];
  }
  [x, y, z, a, b, h] = [
    Number(x),
    Number(y),
    Number(z),
    Number(a),
    Number(b),
    Number(h),
  ];
  if (h <= 0 || a <= 0 || b <= 0) return null;
  for (const p of [x, y, z, a, b, h]) {
    if (isNaN(p)) return null;
  }

  // 在指定坐标创建一个单位立方体
  const material = new THREE.MeshBasicMaterial({
    color: 0x2486b9,
  }); // 创建材质
  const geometry = new THREE.BoxGeometry(a, b, h); // 创建几何体Box
  const cube = new THREE.Mesh(geometry, material); // 组装材质与几何体

  const edgesGeometry = new THREE.EdgesGeometry(geometry, 1); // 从Box中提取边界创建线几何体
  const lineMaterial = new THREE.LineBasicMaterial({
    color: 0x000,
  }); // 创建线材质
  const lineGeometry = new THREE.LineSegments(edgesGeometry, lineMaterial); // 组装线几何体和线材质

  // 设置位置
  cube.position.x = x;
  cube.position.y = y;
  cube.position.z = z;

  cube.add(lineGeometry); // 将线几何体加入到cube几何体

  return cube;
}

function createBallByPositionAndRadius(x, y, z, r) {
  // 验证参数是否合法
  if (arguments.length !== 4) return null;
  [x, y, z, r] = [Number(x), Number(y), Number(z), Number(r)];
  if (r <= 0) return null;
  for (const p of [x, y, z, r]) {
    if (isNaN(p)) return null;
  }

  // 在指定坐标创建一个球体
  const material = new THREE.MeshBasicMaterial({
    color: 0x2486b9,
  }); // 创建材质
  const geometry = new THREE.SphereGeometry(r, 40, 40); // 创建球体Ball
  const ball = new THREE.Mesh(geometry, material); // 组装材质与球体

  const edgesGeometry = new THREE.EdgesGeometry(geometry, 1); // 从Ball中提取边界创建线几何体
  const lineMaterial = new THREE.LineBasicMaterial({
    color: 0x000,
  }); // 创建线材质
  const lineGeometry = new THREE.LineSegments(edgesGeometry, lineMaterial); // 组装线几何体和线材质

  // 设置位置
  ball.position.x = x;
  ball.position.y = y;
  ball.position.z = z;

  ball.add(lineGeometry); // 将线几何体加入到ball几何体

  return ball;
}

function createCylinderByPositionAndSize(x, y, z, r, h) {
  // 验证参数是否合法
  if (arguments.length !== 5) return null;
  [x, y, z, r, h] = [Number(x), Number(y), Number(z), Number(r), Number(h)];
  if (h === 0 || r <= 0) return null;
  for (const p of [x, y, z, r, h]) {
    if (isNaN(p)) return null;
  }

  // 在指定坐标创建一个圆柱体
  const material = new THREE.MeshBasicMaterial({
    color: 0x2486b9,
  }); // 创建材质
  const geometry = new THREE.CylinderGeometry(r, r, h, 25); // 创建几何体cylinder
  const cylinder = new THREE.Mesh(geometry, material); // 组装材质与几何体

  const edgesGeometry = new THREE.EdgesGeometry(geometry, 1); // 从Box中提取边界创建线几何体
  const lineMaterial = new THREE.LineBasicMaterial({
    color: 0x000,
  }); // 创建线材质
  const lineGeometry = new THREE.LineSegments(edgesGeometry, lineMaterial); // 组装线几何体和线材质

  // 设置位置
  cylinder.position.x = x;
  cylinder.position.y = y + h / 2;
  cylinder.position.z = z;

  cylinder.add(lineGeometry); // 将线几何体加入到cube几何体

  return cylinder;
}

function translateGeometryByOffset(id, x, y, z) {
  // 验证参数是否合法
  if (arguments.length !== 4) return null;
  [id, x, y, z] = [Number(id), Number(x), Number(y), Number(z)];
  for (const p of [id, x, y, z]) {
    if (isNaN(p)) return null;
  }
  if (!eventBus.$api.getCanvasGeometrieById(id)) return null;

  const g = eventBus.$api.getCanvasGeometrieById(id);
  g.position.x += x;
  g.position.y += y;
  g.position.z += z;

  createBox3(g);

  eventBus.$emit(THREE_CANVAS_REFRESH);

  return id;
}

function rotateGeometryByIdAndAngle(id, a, b, c) {
  // 验证参数是否合法
  if (arguments.length !== 4) return null;
  [id, a, b, c] = [Number(id), Number(a), Number(b), Number(c)];
  for (const p of [id, a, b, c]) {
    if (isNaN(p)) return null;
  }
  if (!eventBus.$api.getCanvasGeometrieById(id)) return null;

  const g = eventBus.$api.getCanvasGeometrieById(id);
  const x = (a / 180) * Math.PI;
  const y = (b / 180) * Math.PI;
  const z = (c / 180) * Math.PI;
  g.rotateX(x);
  g.rotateY(y);
  g.rotateZ(z);

  createBox3(g);

  eventBus.$emit(THREE_CANVAS_REFRESH);

  return id;
}

function colorGeometryByIdAndColor(id, type, color) {
  if (arguments.length !== 3) return null;
  id = Number(id);
  if (isNaN(id) || !eventBus.$api.getCanvasGeometrieById(id)) return null;
  if (["face", "line"].indexOf(type) === -1) return null;
  if (!/^#([\da-f]{3}|[\da-f]{6})$/.test(color)) return null;

  const g = eventBus.$api.getCanvasGeometrieById(id);

  switch (type) {
    case "face": {
      g.material.color.set(color);
      break;
    }
    case "line": {
      const line = g.children.find(
        (child) => child instanceof THREE.LineSegments
      );
      if (line) line.material.color.set(color);
      else return null;
      break;
    }
    default:
      return null;
  }

  createBox3(g);

  eventBus.$emit(THREE_CANVAS_REFRESH);

  return id;
}

function scaleGeometryByIdAndScale(id, length, width, height) {
  if (arguments.length !== 4) return null;
  [id, length, width, height] = [
    Number(id),
    Number(length),
    Number(width),
    Number(height),
  ];
  for (const p of [id, length, width, height]) {
    if (isNaN(p)) return null;
  }
  if (!eventBus.$api.getCanvasGeometrieById(id)) return null;

  const g = eventBus.$api.getCanvasGeometrieById(id);

  // cube，cylinder，ball意义类似
  g.scale.set(length, width, height);

  createBox3(g);

  eventBus.$emit(THREE_CANVAS_REFRESH);

  return id;
}

function deleteGeometryById(id) {
  // 验证参数是否合法
  if (arguments.length !== 1) return null;
  id = Number(id);
  if (isNaN(id)) return null;
  if (!eventBus.$api.getCanvasGeometrieById(id)) return null;

  eventBus.$emit(THREE_CANVAS_DELETE, id);

  return id;
}

const handlers = {
  add: {
    cube: [createCubeByPositionAndSize],
    ball: [createBallByPositionAndRadius],
    cylinder: [createCylinderByPositionAndSize],
  },
  alter: {
    translation: [translateGeometryByOffset],
    rotate: [rotateGeometryByIdAndAngle],
    color: [colorGeometryByIdAndColor],
    scale: [scaleGeometryByIdAndScale],
  },
  delete: {
    object: [deleteGeometryById],
  },
};

export default handlers;